package logo

import (
	"io"
	"os"
	"sync"
)

type logger struct {
	logFileLine bool
	fmt         Formatter
	logLevel    LogLevel
	printers    []Printer
	mutex       sync.Mutex
	entryPool   sync.Pool
}

const _callDepth = 3

func NewLogger(printers []Printer) *logger {
	return &logger{
		fmt:      TextFormatter,
		logLevel: LevelInfo,
		printers: printers,
	}
}

func (l *logger) newProxyLogger() *ProxyLogger {
	entry, ok := l.entryPool.Get().(*ProxyLogger)
	if ok {
		return entry
	}
	return &ProxyLogger{
		logger: l,
	}
}

func (l *logger) ReleaseProxyLogger(plog *ProxyLogger) {
	plog.resetDataAndError()
	l.entryPool.Put(plog)
}

func (l *logger) With(data interface{}) *ProxyLogger {
	e := l.newProxyLogger()
	return e.With(data)
}

func (l *logger) Debug(v ...interface{}) {
	l.newProxyLogger().log(LevelDebug, _callDepth, v...)
}

func (l *logger) Debugf(format string, v ...interface{}) {
	l.newProxyLogger().logf(LevelDebug, _callDepth, format, v...)
}

func (l *logger) Info(v ...interface{}) {
	l.newProxyLogger().log(LevelInfo, _callDepth, v...)
}

func (l *logger) Infof(format string, v ...interface{}) {
	l.newProxyLogger().logf(LevelInfo, _callDepth, format, v...)
}

func (l *logger) Warn(v ...interface{}) {
	l.newProxyLogger().log(LevelWarn, _callDepth, v...)
}

func (l *logger) Warnf(format string, v ...interface{}) {
	l.newProxyLogger().logf(LevelWarn, _callDepth, format, v...)
}

func (l *logger) Error(v ...interface{}) {
	l.newProxyLogger().log(LevelError, _callDepth, v...)
}

func (l *logger) Errorf(format string, v ...interface{}) {
	l.newProxyLogger().logf(LevelError, _callDepth, format, v...)
}

func (l *logger) SetLevel(level LogLevel) Logger {
	l.mutex.Lock()
	defer l.mutex.Unlock()
	l.logLevel = level
	for n, priner := range l.printers {
		if n >= int(level) {
			priner.SetOutput(os.Stdout)
		} else {
			priner.SetOutput(io.Discard)
		}
	}
	return l
}
func (l *logger) GetLevel() LogLevel { return l.logLevel }

func (l *logger) SetLogFileLine(log bool) Logger {
	l.logFileLine = log
	return l
}

func (l *logger) IsLogFileLine() bool {
	return l.logFileLine
}

func (l *logger) SetFormatter(fmt Formatter) Logger { l.fmt = fmt; return l }

func (l *logger) SetPrinters(printers map[LogLevel]Printer) Logger {
	for level, printer := range printers {
		l.printers[level] = printer
	}
	return l
}
